-
Notifications
You must be signed in to change notification settings - Fork 619
[Dashboard] Update analytics API integration and improve number formatting #8023
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Dashboard] Update analytics API integration and improve number formatting #8023
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
WalkthroughExports formatNumber from "@/lib/search" and adopts it in ContractAnalyticsPage. Multiple analytics utilities switch aggregation from time to day, add dynamic day-window limits, and adjust parsing. Several utilities enhance error handling and response parsing. Total-count endpoints update aggregation aliases/fields and add headers. TransactionsSection adds explicit fetch error checks. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant UI as Contract/Wallet/Tx Analytics UI
participant Util as Analytics Utils
participant API as Insights API
rect rgba(220,235,245,0.4)
note over UI,Util: Daily aggregation flow (changed: group_by=day, limit by daysDifference)
UI->>Util: get*Analytics({ startDate, endDate })
Util->>Util: daysDifference = ceil((end-start)/1d) || 30
Util->>API: GET /insights?...&group_by=day&limit=daysDifference[ * 10 ]
alt response ok
API-->>Util: JSON with day, counts
Util->>Util: Map day->Date, Number(count), sort asc
Util-->>UI: Analytics entries
else error
API-->>Util: Status + text
Util-->>UI: Throw Error("... analytics data: <text>")
end
end
sequenceDiagram
autonumber
participant UI as Totals UI
participant Util as Totals Utils
participant API as Insights API
rect rgba(235,245,220,0.4)
note over UI,Util: Totals flow (changed: count field, auth header)
UI->>Util: getTotal*()
Util->>API: GET /insights?...&aggregate=count()[ + x-client-id ]
alt ok
API-->>Util: JSON aggregations[0][0].count
Util-->>UI: { count }
else not ok
API-->>Util: Status + text
Util-->>UI: Throw Error("...: <text>")
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Pre-merge checks (1 passed, 2 warnings)❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (1)
✨ Finishing Touches
🧪 Generate unit tests
Comment |
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8023 +/- ##
==========================================
- Coverage 56.65% 56.62% -0.03%
==========================================
Files 904 904
Lines 58677 58677
Branches 4165 4161 -4
==========================================
- Hits 33241 33225 -16
- Misses 25330 25346 +16
Partials 106 106
🚀 New features to boost your workflow:
|
| } | ||
|
|
||
| const json = (await res.json()) as InsightResponse; | ||
| console.log("wallet analytics json", json); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This console.log statement should be removed before merging as it appears to be debugging code. Logging large JSON objects to the console in production code can impact performance and potentially expose sensitive data in browser developer tools.
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
size-limit report 📦
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (21)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-events.ts (1)
5-17: Align InsightResponse type with day-based groupingResponse parsing checks "day", but the type still models "time". Update the type to prevent drift.
type InsightResponse = { aggregations: [ Record< string, | { - time: string; + day: string; count: number; } | unknown >, ]; };apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-unique-wallets.ts (2)
36-41: Include HTTP status code in thrown errorAdds quick visibility into failures without digging into logs.
- if (!res.ok) { - const errorText = await res.text(); - throw new Error( - `Failed to fetch unique wallets analytics data: ${errorText}`, - ); - } + if (!res.ok) { + const errorText = await res.text(); + throw new Error( + `Failed to fetch unique wallets analytics data: ${res.status} ${res.statusText} - ${errorText}`, + ); + }
45-47: Guard against missing aggregation; default to 0Prevents runtime errors if the API returns an empty aggregation.
- return { - count: json.aggregations[0][0].count, - }; + return { + count: json.aggregations?.[0]?.[0]?.count ?? 0, + };apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-contract-events.ts (1)
35-38: Surface HTTP status in errorConsistent with other modules and improves debuggability.
- if (!res.ok) { - const errorText = await res.text(); - throw new Error(`Failed to fetch events analytics data: ${errorText}`); - } + if (!res.ok) { + const errorText = await res.text(); + throw new Error( + `Failed to fetch events analytics data: ${res.status} ${res.statusText} - ${errorText}`, + ); + }apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-contract-transactions.ts (1)
35-39: Add HTTP status to errorBrings this in line with other improved error messages.
- if (!res.ok) { - const errorText = await res.text(); - throw new Error( - `Failed to fetch transactions analytics data: ${errorText}`, - ); - } + if (!res.ok) { + const errorText = await res.text(); + throw new Error( + `Failed to fetch transactions analytics data: ${res.status} ${res.statusText} - ${errorText}`, + ); + }apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/TransactionsSection/TransactionsSection.tsx (2)
165-208: Set sensible cache staleness per guidelinesReact Query defaults to staleTime 0. Dashboard guidelines recommend ≥60s.
const txQuery = useQuery({ enabled: !!account && !!activeChain, queryFn: async () => { if (!account || !activeChain) { return []; } @@ }, queryKey: ["v1/wallets/transactions", account?.address, activeChain?.id], - retry: false, + retry: false, + staleTime: 60_000, + cacheTime: 5 * 60_000, });
1-11: Expose className on UI component root and merge via cn()Matches the apps/dashboard TSX guideline and improves composability.
+import { cn } from "@/lib/utils"; @@ -export function TransactionSectionUI(props: { - data: WalletTransaction[]; - isPending: boolean; - client: ThirdwebClient; -}) { +export function TransactionSectionUI(props: { + data: WalletTransaction[]; + isPending: boolean; + client: ThirdwebClient; + className?: string; +}) { @@ - return ( - <div className="flex flex-col gap-1"> + return ( + <div className={cn("flex flex-col gap-1", props.className)}>Also applies to: 26-60
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-function-breakdown.ts (3)
29-35: Clamp daysDifference to at least 1 to avoid limit=0When startDate === endDate, ceil(...) can be 0 leading to no results.
- const daysDifference = - params.startDate && params.endDate - ? Math.ceil( - (params.endDate.getTime() - params.startDate.getTime()) / - (1000 * 60 * 60 * 24), - ) - : 30; + const daysDifference = + params.startDate && params.endDate + ? Math.max( + 1, + Math.ceil( + (params.endDate.getTime() - params.startDate.getTime()) / + (1000 * 60 * 60 * 24), + ), + ) + : 30;
60-62: Preserve server error details in thrown errorAlign with other utils by including status and body.
- if (!res.ok) { - throw new Error("Failed to fetch analytics data"); - } + if (!res.ok) { + const errorText = await res.text(); + throw new Error( + `Failed to fetch function breakdown analytics data: ${res.status} ${res.statusText} - ${errorText}`, + ); + }
12-14: Update response typing to day-based shapeKeeps types in sync with "group_by=day".
-// This is weird aggregation response type, this will be changed later in insight -type InsightResponse = { - aggregations: [Record<string, InsightAggregationEntry | unknown>]; -}; +// This is weird aggregation response type, this will be changed later in insight +type InsightResponse = { + aggregations: [ + Record<string, { function_selector: string; day: string; count: number } | unknown> + ]; +};apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-transactions.ts (3)
33-39: Clamp daysDifference >= 1Prevents limit=0 when dates are equal.
- const daysDifference = - params.startDate && params.endDate - ? Math.ceil( - (params.endDate.getTime() - params.startDate.getTime()) / - (1000 * 60 * 60 * 24), - ) - : 30; + const daysDifference = + params.startDate && params.endDate + ? Math.max( + 1, + Math.ceil( + (params.endDate.getTime() - params.startDate.getTime()) / + (1000 * 60 * 60 * 24), + ), + ) + : 30;
63-66: Include status in error messageMatches other utils and aids debugging.
- if (!res.ok) { - const errorText = await res.text(); - throw new Error(`Failed to fetch transaction analytics data: ${errorText}`); - } + if (!res.ok) { + const errorText = await res.text(); + throw new Error( + `Failed to fetch transaction analytics data: ${res.status} ${res.statusText} - ${errorText}`, + ); + }
5-17: Type InsightResponse to "day" bucketsRuntime checks look for "day"; update the type accordingly.
type InsightResponse = { aggregations: [ Record< string, | { - time: string; + day: string; count: number; } | unknown >, ]; };apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-wallet-analytics.ts (5)
33-39: Clamp daysDifference >= 1Same rationale as other analytics utils.
- const daysDifference = - params.startDate && params.endDate - ? Math.ceil( - (params.endDate.getTime() - params.startDate.getTime()) / - (1000 * 60 * 60 * 24), - ) - : 30; + const daysDifference = + params.startDate && params.endDate + ? Math.max( + 1, + Math.ceil( + (params.endDate.getTime() - params.startDate.getTime()) / + (1000 * 60 * 60 * 24), + ), + ) + : 30;
64-67: Include HTTP status in errorConsistent with other modules.
- if (!res.ok) { - const errorText = await res.text(); - throw new Error(`Failed to fetch wallet analytics data: ${errorText}`); - } + if (!res.ok) { + const errorText = await res.text(); + throw new Error( + `Failed to fetch wallet analytics data: ${res.status} ${res.statusText} - ${errorText}`, + ); + }
70-70: Remove console.log or gate to non-productionAvoids noisy logs and possible data leakage in prod.
- console.log("wallet analytics json", json); + if (process.env.NODE_ENV !== "production") { + // eslint-disable-next-line no-console + console.debug("wallet analytics json", json); + }
11-13: Type InsightResponse to "day" bucketsMatches the checks for tx.day.
| { - time: string; + day: string; count: number; }
33-45: De-duplicate shared analytics query mathdaysDifference/group_by/limit logic is repeated across multiple utils. Consider a small shared helper (e.g., "@/utils/analytics.ts") to compute days window and build query params; reduces drift.
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/ContractAnalyticsPage.tsx (2)
13-13: Avoid importing a tiny util from a heavy module to keep the client bundle lean
@/lib/searchpulls inthirdweb/chains, invariant, and search helpers. Importing it in a client component just forformatNumberrisks pulling unnecessary code into the bundle. ExtractformatNumberinto a lightweight util (e.g.,@/lib/number) and import from there; optionally re-export from@/lib/searchto preserve the API.Apply this diff locally:
-import { formatNumber } from "@/lib/search"; +import { formatNumber } from "@/lib/number";New file (lightweight):
// apps/dashboard/src/@/lib/number.ts export const formatNumber = (num: number): string => { if (num >= 1_000_000) { return `${(num / 1_000_000).toLocaleString(undefined, { maximumFractionDigits: 1, minimumFractionDigits: 1, })}M`; } if (num >= 1_000) { return `${(num / 1_000).toLocaleString(undefined, { maximumFractionDigits: 1, minimumFractionDigits: 1, })}k`; } return num.toLocaleString(undefined, { maximumFractionDigits: num < 10 ? 2 : 1, minimumFractionDigits: 0, }); };Optional: keep existing public surface by re-exporting in
@/lib/search.ts:// apps/dashboard/src/@/lib/search.ts export { formatNumber } from "./number";
378-381: Type the render callback param for clarityAnnotate
vto satisfy our explicit return/param typing guideline and improve readability.- render={(v) => { + render={(v: number) => { return <dd className="font-normal text-xl">{formatNumber(v)}</dd>; }}apps/dashboard/src/@/lib/search.ts (1)
187-205: Export looks good; add explicit return type and consider extended suffixesAdd an explicit return type per our TS guidelines. Optionally extend to handle billions/trillions and negatives via
Math.absfor thresholds.Minimal typing fix:
-export const formatNumber = (num: number) => { +export const formatNumber = (num: number): string => {Optional extended formatter:
-export const formatNumber = (num: number): string => { - if (num >= 1000000) { - return `${(num / 1000000).toLocaleString(undefined, { +export const formatNumber = (num: number): string => { + const n = Math.abs(num); + const sign = num < 0 ? "-" : ""; + if (n >= 1_000_000_000_000) { + return `${sign}${(n / 1_000_000_000_000).toLocaleString(undefined, { + maximumFractionDigits: 1, + minimumFractionDigits: 1, + })}T`; + } + if (n >= 1_000_000_000) { + return `${sign}${(n / 1_000_000_000).toLocaleString(undefined, { + maximumFractionDigits: 1, + minimumFractionDigits: 1, + })}B`; + } + if (n >= 1_000_000) { + return `${sign}${(n / 1_000_000).toLocaleString(undefined, { maximumFractionDigits: 1, minimumFractionDigits: 1, })}M`; } - if (num >= 1000) { - return `${(num / 1000).toLocaleString(undefined, { + if (n >= 1_000) { + return `${sign}${(n / 1_000).toLocaleString(undefined, { maximumFractionDigits: 1, minimumFractionDigits: 1, })}k`; } - return num.toLocaleString(undefined, { - maximumFractionDigits: num < 10 ? 2 : 1, + return `${sign}${n.toLocaleString(undefined, { + maximumFractionDigits: n < 10 ? 2 : 1, minimumFractionDigits: 0, - }); + })}`; };If you extract
formatNumberinto a lightweight@/lib/numberas suggested, re-export here to keep the public API stable:export { formatNumber } from "./number";
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (10)
apps/dashboard/src/@/lib/search.ts(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/ContractAnalyticsPage.tsx(2 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-events.ts(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-function-breakdown.ts(2 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-transactions.ts(3 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-wallet-analytics.ts(3 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-contract-events.ts(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-contract-transactions.ts(3 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-unique-wallets.ts(3 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/TransactionsSection/TransactionsSection.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/typesor localtypes.tsbarrels
Prefer type aliases over interface except for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial,Pick, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
**/*.{ts,tsx}: Use explicit function declarations and explicit return types in TypeScript
Limit each file to one stateless, single‑responsibility function
Re‑use shared types from@/typeswhere applicable
Prefertypealiases overinterfaceexcept for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Prefer composition over inheritance; use utility types (Partial, Pick, etc.)
Lazy‑import optional features and avoid top‑level side‑effects to reduce bundle size
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-contract-events.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-unique-wallets.tsapps/dashboard/src/@/lib/search.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-transactions.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/ContractAnalyticsPage.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/TransactionsSection/TransactionsSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-events.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-contract-transactions.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-function-breakdown.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-wallet-analytics.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-contract-events.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-unique-wallets.tsapps/dashboard/src/@/lib/search.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-transactions.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/ContractAnalyticsPage.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/TransactionsSection/TransactionsSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-events.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-contract-transactions.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-function-breakdown.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-wallet-analytics.ts
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/*(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLinkfor internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()from@/lib/utilsfor conditional class logic
Use design system tokens (e.g.,bg-card,border-border,text-muted-foreground)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()to retrieve JWT from cookies on server side
UseAuthorization: Bearerheader – never embed tokens in URLs
Return typed results (e.g.,Project[],User[]) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stablequeryKeysfor React Query cache hits
ConfigurestaleTime/cacheTimein React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-jsin server components
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-contract-events.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-unique-wallets.tsapps/dashboard/src/@/lib/search.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-transactions.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/ContractAnalyticsPage.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/TransactionsSection/TransactionsSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-events.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-contract-transactions.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-function-breakdown.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-wallet-analytics.ts
apps/{dashboard,playground}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
apps/{dashboard,playground}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/_(e.g., Button, Input, Tabs, Card)
UseNavLinkfor internal navigation to get active state handling
Use Tailwind CSS for styling; no inline styles
Merge class names withcn()from@/lib/utilsfor conditional classes
Stick to design tokens (e.g., bg-card, border-border, text-muted-foreground)
Server Components must start withimport "server-only"; usenext/headers, server‑only env, heavy data fetching, andredirect()where appropriate
Client Components must start with'use client'; handle interactivity with hooks and browser APIs
Server-side data fetching: callgetAuthToken()from cookies, sendAuthorization: Bearer <token>header, and return typed results (avoidany)
Client-side data fetching: wrap calls in React Query with descriptive, stablequeryKeysand set sensiblestaleTime/cacheTime(≥ 60s default); keep tokens secret via internal routes or server actions
Do not importposthog-jsin server components (client-side only)
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-contract-events.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-unique-wallets.tsapps/dashboard/src/@/lib/search.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-transactions.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/ContractAnalyticsPage.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/TransactionsSection/TransactionsSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-events.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-contract-transactions.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-function-breakdown.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-wallet-analytics.ts
apps/{dashboard,playground}/**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Expose a
classNameprop on the root element of every component
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/ContractAnalyticsPage.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/TransactionsSection/TransactionsSection.tsx
🧠 Learnings (3)
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to src/@/analytics/report.ts : Analytics event name: human-readable `<subject> <verb>` (e.g., "contract deployed"); function: `report<Subject><Verb>` (PascalCase)
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-contract-events.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-events.ts
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to src/@/analytics/report.ts : Review `src/@/analytics/report.ts` before adding analytics events to check for duplicates
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-contract-events.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/ContractAnalyticsPage.tsx
📚 Learning: 2025-08-29T15:37:38.513Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: AGENTS.md:0-0
Timestamp: 2025-08-29T15:37:38.513Z
Learning: Applies to apps/{dashboard,playground}/src/@/analytics/report.ts : Analytics naming: event name as `<subject> <verb>`; helper function as `report<Subject><Verb>` (PascalCase)
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-events.ts
🧬 Code graph analysis (1)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/ContractAnalyticsPage.tsx (1)
apps/dashboard/src/@/lib/search.ts (1)
formatNumber(187-204)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Size
🔇 Additional comments (3)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-events.ts (1)
62-65: Clearer error message — LGTMThe more specific failure text helps triage.
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-contract-transactions.ts (1)
22-24: Query simplification — LGTMSwitching to aggregate=count() is cleaner and matches the returned shape.
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/TransactionsSection/TransactionsSection.tsx (1)
194-198: Explicit non-ok handling — LGTMGood call to throw with status and body; avoids parsing bogus JSON.

PR-Codex overview
This PR focuses on improving error handling and code consistency in various analytics utility functions, while also enhancing the formatting of numbers for display. It introduces better error messages and modifies the structure of some data being returned.
Detailed summary
formatNumberto be an exported function insearch.ts.countinstead oftotal.timetodayin certain analytics queries.daysDifferencefor analytics queries.Summary by CodeRabbit
New Features
Bug Fixes
Refactor
Chores